﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ParseXAML
{
#if ANALYSE_ASSEMBLY
    public class Reference
    {
        public string AssemblyName { get; set; }
        public string FullName { get; set; }

        public Reference(string assemblyName, string fullName)
        {
            this.AssemblyName = assemblyName;
            this.FullName = fullName;
        }
    }

    internal class xmlnsDefinition
    {
        internal string ClrNamespace;
        internal string XmlNamespace;
    }

    internal class xmlnsDefinitions : List<xmlnsDefinition>
    {
        internal List<string> GetNameSpace(string nameUri)
        {
            return (from l in this
                    where (l.XmlNamespace.ToLower() == nameUri.ToLower())
                    select l.ClrNamespace).ToList();
        }
    }

    internal class AssemblyLoaded
    {
        internal string Name;
        internal System.Reflection.Assembly Assembly;
        internal xmlnsDefinitions xmlnsDefinitions = new xmlnsDefinitions();

        internal AssemblyLoaded(string Name, System.Reflection.Assembly assembly)
        {
            this.Name = Name;
            this.Assembly = assembly;
            foreach (var o in assembly.GetCustomAttributes(typeof(System.Windows.Markup.XmlnsDefinitionAttribute), false))
            {
                System.Windows.Markup.XmlnsDefinitionAttribute attr = o as System.Windows.Markup.XmlnsDefinitionAttribute;
                xmlnsDefinitions.Add(new xmlnsDefinition() { ClrNamespace = attr.ClrNamespace, XmlNamespace = attr.XmlNamespace });
            }
        }
    }

    internal class LocalizableAssembly : IDisposable
    {
        protected List<Reference> references;

        protected Global global;

        protected AppDomain _myDomain;

        internal protected List<AssemblyLoaded> assemblies = new List<AssemblyLoaded>();

        protected AppDomain myDomain
        {
            get
            {
                if (_myDomain == null)
                {
                    AppDomainSetup setup = new AppDomainSetup();
                    setup.ApplicationBase = global.FullOutputPath;

                    _myDomain = AppDomain.CreateDomain("XamlLocalizeDomain",
                        null,
                        setup);
                    //                    _myDomain.AssemblyResolve += new ResolveEventHandler(_myDomain_AssemblyResolve);
                }
                return _myDomain;
            }
        }

        internal LocalizableAssembly(Global global, List<Reference> references)
        {
            this.global = global;
            this.references = references;
        }

        internal System.Reflection.Assembly LoadAssembly(string assemblyName)
        {
            try
            {
                string[] assemblyNameParts = assemblyName.Split(',');
                System.Reflection.Assembly assembly = null;
                string dllName = assemblyNameParts[0].ToLower();

                var loaded = (from l in this.assemblies
                              where (l.Name == dllName)
                              select l).ToList();
                if (loaded.Count > 0)
                {
                    return loaded[0].Assembly;
                }

                string fileName;
                if (System.IO.Path.GetExtension(dllName).ToLower() == ".exe")
                {
                    fileName = System.IO.Path.Combine(global.FullOutputPath, dllName);
                }
                else
                {
                    fileName = System.IO.Path.Combine(global.FullOutputPath, dllName + ".dll");
                }

                if (System.IO.File.Exists(fileName))
                {
                    System.Reflection.AssemblyName aName = new System.Reflection.AssemblyName(assemblyName);
                    aName.CodeBase = fileName;
                    assembly = myDomain.Load(aName);
                }
                else
                {
                    if (this.references != null)
                    {
                        List<Reference> o = (from l in this.references
                                             where (l.AssemblyName.ToLower() == dllName)
                                             select l).ToList();
                        if (o.Count > 1)
                        {
                            this.global.AddMessage(Message.eType.Warning, "Multiple possibilities to load {0}, we load {1}", dllName, o[0].FullName);
                        }
                        if (o.Count > 0)
                        {
                            System.Reflection.AssemblyName aName = new System.Reflection.AssemblyName(assemblyName);
                            aName.CodeBase = o[0].FullName;
                            assembly = myDomain.Load(aName);
                        }
                    }
                    if (assembly == null)
                    { // the GAC ?
                        assembly = myDomain.Load(assemblyName + ".dll");
                    }
                }
                // XmlnsDefinitionAttribute
                if (assembly != null)
                {
                    assemblies.Add(new AssemblyLoaded(dllName, assembly));
                    this.global.AddMessage(Message.eType.Nothing, "Assembly {0} is loaded", dllName);
                }
                else
                {
                    this.global.AddMessage(Message.eType.Error, "Unable to load Assembly {0}", dllName);
                }

                return assembly;
            }
            catch (Exception ex)
            {
                this.global.AddMessage(Message.eType.Error, "Error while loading {0} : {1}", assemblyName, ex.ToString());
                return null;
            }
        }


        internal Type GetType(NamespaceAssociation na, string typeName)
        {
            if ((this.assemblies.Count == 0) && (this.references != null))
            {
                foreach (var l in this.references)
                {
                    LoadAssembly(l.AssemblyName);
                }
            }
            System.Reflection.Assembly assembly = null;
            Type typ = null;
            if (na.IsUri)
            {
                string nameControl = typeName;
                int indexPoint = typeName.LastIndexOf('.');
                if (indexPoint >= 0)
                    nameControl = typeName.Substring(indexPoint + 1);
                // bon alors, on cherche dans tous les assemblies ceux qui peuvent être associés à l'URI
                // sachant que c'est une relation m vers m (multiple assembly avec chacun plusieurs namespace possible)
                foreach (var l in this.assemblies)
                {
                    List<string> listNamespace = l.xmlnsDefinitions.GetNameSpace(na.AssemblyName);
                    foreach (var myNamespace in listNamespace)
                    {
                        typ = l.Assembly.GetType(string.Concat(myNamespace, ".", nameControl));
                        if (typ != null)
                            break;
                    }
                    if (typ != null)
                        break;
                }
            }
            else
            {
                assembly = LoadAssembly(na.AssemblyName);
                if (assembly != null)
                {
                    typ = assembly.GetType(typeName);
                }
            }
            if (typ == null)
            {
                this.global.AddMessage(Message.eType.Error, "Unable to find type {0}", typeName);
            }
            return typ;
        }

    #region IDisposable Members

        public void Dispose()
        {
            if (this._myDomain != null)
            {
                //                _myDomain.AssemblyResolve -= _myDomain_AssemblyResolve;
                AppDomain.Unload(this._myDomain);
            }
        }

        #endregion
    }
#else
    internal class LocalizableAssembly
    {
    }
#endif
}